<template>
  <div ref="wraper" :class="wraperClasses" @DOMMouseScroll="handleDOMMouseWheel" @mousewheel="handleMouseWheel">
    <div
      ref="content"
      :class="`${prefix}-content`"
      :style="contentStyles"
    >
      <slot></slot>
    </div>
    <div v-show="percentY < 1 && !disScrollY" :class="scrollYClasses" :style="scrollYStyles">
      <div :class="[`${prefix}-scroll-bar`, `${prefix}-scroll-bar-y`]" :style="scrollBarYStyles" @mousedown="handleMousedownScrollBarY"></div>
    </div>
    <div v-show="percentX < 1 && !disScrollX" :class="scrollXClasses" :style="scrollXStyles">
      <div :class="[`${prefix}-scroll-bar`, `${prefix}-scroll-bar-x`]" :style="scrollBarXStyles" @mousedown="handleMousedownScrollBarX"></div>
    </div>
    <div v-show="percentX < 1 && percentY < 1" :class="`${prefix}-place-holder`"></div>
  </div>
</template>

<script>
/**
 * 参考iview-admin
 */
export default {
  name: 'scrollBar',
  props: {
    speed: {
      type: Number,
      default: 20
    },
    scrollXStyle: {
      type: Object,
      default () {
        return {};
      }
    },
    disScrollX: {
      type: Boolean,
      default: false
    },
    disScrollY: {
      type: Boolean,
      default: false
    },
    scrollYStyle: {
      type: Object,
      default () {
        return {};
      }
    },
    scrollXType: {
      type: String,
      default: 'cover'
    },
    scrollYType: {
      type: String,
      default: 'cover'
    },
    showAll: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      prefix: 'vue-scroller-bars',
      scrollOffsetX: 0,
      scrollOffsetY: 0,
      contentSize: {
        width: 0,
        height: 0
      },
      wraperSize: {
        width: 0,
        height: 0
      },
      initY: 0,
      initOffsetY: 0,
      initX: 0,
      initOffsetX: 0
    };
  },
  computed: {
    wraperClasses () {
      return [
        `${this.prefix}-wraper`,
        this.showAll ? '' : 'show-when-hover'
      ];
    },
    contentStyles () {
      return {
        transform: `translate3d(-${this.scrollOffsetX}px, -${this.scrollOffsetY}px, 0px)`,
        width: this.disScrollX ? '100%' : 'auto',
        height: this.disScrollY ? '100%' : 'auto'
      };
    },
    percentY () {
      return this.wraperSize.height / this.contentSize.height;
    },
    scrollYStyles () {
      return Object.assign(this.scrollYStyle, {
        height: this.percentX < 1 ? 'calc(100% - 14px)' : '100%'
      });
    },
    scrollBarYHeight () {
      return this.percentY * (this.wraperSize.height - 2);
    },
    scrollBarYStyles () {
      let height = this.scrollBarYHeight;
      return {
        transform: `translate3d(0px, ${this.scrollOffsetY * (height / this.wraperSize.height)}px, 0px)`,
        height: `${height}px`
      };
    },
    scrollYClasses () {
      return [
        `${this.prefix}-scroll`,
        `${this.prefix}-scroll-y`,
        this.scrollYType === 'cover' ? 'scroll-y-cover' : ''
      ];
    },
    gapY () {
      return this.contentSize.height - this.wraperSize.height;
    },
    percentX () {
      return this.wraperSize.width / this.contentSize.width;
    },
    scrollXStyles () {
      return Object.assign(this.scrollXStyle, {
        width: this.percentY < 1 ? 'calc(100% - 14px)' : '100%'
      });
    },
    scrollBarXWidth () {
      return this.percentX * (this.wraperSize.width - 2);
    },
    scrollBarXStyles () {
      let width = this.scrollBarXWidth;
      return {
        transform: `translate3d(${this.scrollOffsetX * (width / this.wraperSize.width)}px, 0px, 0px)`,
        width: `${width}px`
      };
    },
    scrollXClasses () {
      return [
        `${this.prefix}-scroll`,
        `${this.prefix}-scroll-x`,
        this.scrollXType === 'cover' ? 'scroll-x-cover' : ''
      ];
    },
    gapX () {
      return this.contentSize.width - this.wraperSize.width;
    }
  },
  methods: {
    resize () {
      this.$nextTick(() => {
        let wraperRect = this.$refs.wraper.getBoundingClientRect();
        let contentRect = this.$refs.content.getBoundingClientRect();
        this.contentSize = {
          width: contentRect.width,
          height: contentRect.height
        };
        let percentXLowerThanOne = (wraperRect.width / contentRect.width) < 1;
        let percentYLowerThanOne = (wraperRect.height / contentRect.height) < 1;
        let gap = percentXLowerThanOne && percentYLowerThanOne ? 14 : 0;
        this.wraperSize = {
          width: wraperRect.width - gap,
          height: wraperRect.height - gap
        };
        if (this.contentSize.height <= this.wraperSize.height) { // if content's height shorter than wraper's height
          this.scrollOffsetY = 0;
        };
        if ((wraperRect.bottom > contentRect.bottom) && (this.scrollOffsetY > 0)) { // if content's bottom upper wraper's bottom
          this.scrollOffsetY += contentRect.bottom - wraperRect.bottom;
        };
      });
    },
    handleMouseWheel (e) {
      this.scrollOffsetY += this.percentY < 1 ? e.deltaY : 0;
      this.scrollOffsetX += this.percentX < 1 ? e.deltaX : 0;
      if (this.percentY < 1) {
        if (this.scrollOffsetY >= this.gapY) {
          this.scrollOffsetY = Math.min(this.gapY, this.scrollOffsetY);
        } else if (this.scrollOffsetY <= 0) {
          this.scrollOffsetY = Math.max(this.scrollOffsetY, 0);
        }
      }
      if (this.percentX < 1) {
        if (this.scrollOffsetX >= this.gapX) {
          this.scrollOffsetX = Math.min(this.gapX, this.scrollOffsetX);
        } else if (this.scrollOffsetX <= 0) {
          this.scrollOffsetX = Math.max(this.scrollOffsetX, 0);
        }
      }
    },
    handleDOMMouseWheel (e) {
      this.scrollOffsetY += e.detail * 16;
      if (this.scrollOffsetY >= this.gapY) {
        this.scrollOffsetY = Math.min(this.gapY, this.scrollOffsetY);
      } else if (this.scrollOffsetY <= 0) {
        this.scrollOffsetY = Math.max(this.scrollOffsetY, 0);
      }
    },
    handleMousemoveY (e) {
      let offset = e.pageY - this.initY;
      this.scrollOffsetY = this.initOffsetY + offset / ((this.wraperSize.height - 2 - this.scrollBarYHeight) / (this.contentSize.height - this.wraperSize.height));
      if (this.scrollOffsetY >= this.gapY) {
        this.scrollOffsetY = Math.min(this.gapY, this.scrollOffsetY);
      } else if (this.scrollOffsetY <= 0) {
        this.scrollOffsetY = Math.max(this.scrollOffsetY, 0);
      }
      e.preventDefault();
    },
    handleMousedownScrollBarY (e) {
      this.initY = e.pageY;
      this.initOffsetY = this.scrollOffsetY;
      document.addEventListener('mousemove', this.handleMousemoveY);
      document.addEventListener('mouseup', this.handleMouseup);
    },
    handleMousemoveX (e) {
      let offset = e.pageX - this.initX;
      this.scrollOffsetX = this.initOffsetX + offset / ((this.wraperSize.width - 2 - this.scrollBarXWidth) / (this.contentSize.width - this.wraperSize.width));
      if (this.scrollOffsetX >= this.gapX) {
        this.scrollOffsetX = Math.min(this.gapX, this.scrollOffsetX);
      } else if (this.scrollOffsetX <= 0) {
        this.scrollOffsetX = Math.max(this.scrollOffsetX, 0);
      }
      e.preventDefault();
    },
    handleMousedownScrollBarX (e) {
      this.initX = e.pageX;
      this.initOffsetX = this.scrollOffsetX;
      document.addEventListener('mousemove', this.handleMousemoveX);
      document.addEventListener('mouseup', this.handleMouseup);
    },
    handleMouseup (e) {
      document.removeEventListener('mousemove', this.handleMousemoveY);
      document.removeEventListener('mousemove', this.handleMousemoveX);
      document.removeEventListener('mousemove', this.handleMouseup);
    }
  },
  mounted () {
    this.resize();
  }
};
</script>

<style lang="less">
  @import 'scroll-bar.less';
</style>
